<!DOCTYPE html>












  


<html class="theme-next pisces use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2"/>
<meta name="theme-color" content="#222">












<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />






















<link href="/my_blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/my_blog/css/main.css?v=6.3.0" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/my_blog/images/apple-touch-icon-next.png?v=6.3.0">


  <link rel="icon" type="image/png" sizes="32x32" href="/my_blog/images/favicon-32x32-next.png?v=6.3.0">


  <link rel="icon" type="image/png" sizes="16x16" href="/my_blog/images/favicon-16x16-next.png?v=6.3.0">


  <link rel="mask-icon" href="/my_blog/images/logo.svg?v=6.3.0" color="#222">









<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/my_blog/',
    scheme: 'Pisces',
    version: '6.3.0',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: false,
    fastclick: false,
    lazyload: false,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>


  




  <meta property="og:type" content="website">
<meta property="og:title" content="Raymond&#39;s Blog">
<meta property="og:url" content="http://sdfsd23few23.gitee.io/my_blog/index.html">
<meta property="og:site_name" content="Raymond&#39;s Blog">
<meta property="og:locale" content="zh-Hans">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Raymond&#39;s Blog">






  <link rel="canonical" href="http://sdfsd23few23.gitee.io/my_blog/"/>



<script type="text/javascript" id="page.configurations">
  CONFIG.page = {
    sidebar: "",
  };
</script>

  <title>Raymond's Blog</title>
  









  <noscript>
  <style type="text/css">
    .use-motion .motion-element,
    .use-motion .brand,
    .use-motion .menu-item,
    .sidebar-inner,
    .use-motion .post-block,
    .use-motion .pagination,
    .use-motion .comments,
    .use-motion .post-header,
    .use-motion .post-body,
    .use-motion .collection-title { opacity: initial; }

    .use-motion .logo,
    .use-motion .site-title,
    .use-motion .site-subtitle {
      opacity: initial;
      top: initial;
    }

    .use-motion {
      .logo-line-before i { left: initial; }
      .logo-line-after i { right: initial; }
    }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left 
  page-home">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/my_blog/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Raymond's Blog</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
    
      
        <p class="site-subtitle">CSharp & Java</p>
      
    
  </div>

  <div class="site-nav-toggle">
    <button aria-label="Toggle navigation bar">
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>



<nav class="site-nav">
  
    <ul id="menu" class="menu">
      
        
        
        
          
          <li class="menu-item menu-item-home">
    <a href="/my_blog/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-home"></i> <br />Startseite</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-tags">
    <a href="/my_blog/tags/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />Tags</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-categories">
    <a href="/my_blog/categories/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-th"></i> <br />Kategorien</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-archives">
    <a href="/my_blog/archives/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />Archiv</a>
  </li>

      
      
    </ul>
  

  

  
</nav>



  



</div>
    </header>

    


    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          
            

          
          <div id="content" class="content">
            
  <section id="posts" class="posts-expand">
    
      

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://sdfsd23few23.gitee.io/my_blog/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Raymond">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/my_blog/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Raymond's Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/" itemprop="url">
                  Memory Management and Garbage Collection in .NET - 2
                </a>
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Veröffentlicht am</span>
              

              
                
              

              <time title="Post created: 2018-08-10 20:45:56" itemprop="dateCreated datePublished" datetime="2018-08-10T20:45:56+08:00">2018-08-10</time>
            

            
              

              
                
                <span class="post-meta-divider">|</span>
                

                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                
                  <span class="post-meta-item-text">Edited on</span>
                
                <time title="Updated at: 2018-08-11 14:24:29" itemprop="dateModified" datetime="2018-08-11T14:24:29+08:00">2018-08-11</time>
              
            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">in</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/my_blog/categories/Coding/" itemprop="url" rel="index"><span itemprop="name">Coding</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p>#.NET 中的内存管理和垃圾回收机制 (1) - 垃圾回收</p>
<p>本文参考来源：<a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/memory-management-and-gc" target="_blank" rel="noopener">Memory Management and Garbage Collection in .NET</a> </p>
<p>每次创建新对象时，CLR 都会从托管堆中为该对象分配内存。 </p>
<p>只要托管堆中的地址空间可用，运行时就会继续为新对象分配空间。 </p>
<p>但是，内存不是无限的。最终垃圾收集器必须执行一个回收才能释放一些内存。 </p>
<p>垃圾收集器的优化引擎根据正在进行的内存分配确定执行收集的最佳时间。 </p>
<p>当垃圾收集器执行集合时，它会检查托管堆中不再被应用程序使用的对象，并执行必要的操作来回收其内存。</p>
<h2 id="垃圾回收基础原理"><a href="#垃圾回收基础原理" class="headerlink" title="垃圾回收基础原理"></a>垃圾回收基础原理</h2><h3 id="内存基础原理"><a href="#内存基础原理" class="headerlink" title="内存基础原理"></a>内存基础原理</h3><p>以下列表总结了重要的 CLR 内存概念：</p>
<ol>
<li><p>每个进程都有自己独立的虚拟地址空间。同一台计算机上的所有进程共享相同的物理内存，并共享页面文件（如果有）。</p>
</li>
<li><p>默认情况下，在32位计算机上，每个进程都有一个 2GB 的虚拟地址空间。</p>
</li>
<li><p>作为应用程序开发人员，您只能使用虚拟地址空间，而不能直接操作物理内存。垃圾收集器在托管堆上为您分配和释放虚拟内存。<br>如果您正在编写本机代码，则使用 Win32 函数来处理虚拟地址空间。这些函数在本机堆上为您分配和释放虚拟内存。</p>
</li>
<li><p>虚拟内存可以处于三种状态：</p>
<ul>
<li><p>Free。内存块没有引用它，可用于分配。</p>
</li>
<li><p>Reserved。内存块可供您使用，不能用于任何其他分配请求。但是，在提交之前，您无法将数据存储到此内存块。</p>
</li>
<li><p>Committed。内存块分配给物理存储。</p>
</li>
</ul>
</li>
<li><p>虚拟地址空间可能会碎片化。这意味着地址空间中有空闲块，也称为空洞。当请求分配虚拟内存时，虚拟内存管理器必须找到足够大的单个空闲块以满足该分配请求。即使您有 2GB 的可用空间，除非所有可用空间都在一个地址块中，否则一个 2GB 的分配将不会成功。</p>
</li>
<li><p>如果用尽预留的虚拟地址空间或要提交的物理空间，则可能会耗尽内存。</p>
</li>
</ol>
<p>即使即物理内存需求较低，也会使用您的页面文件。 物理内存需求较高时，操作系统必须在物理内存中腾出空间来存储数据，并将物理内存中的一些数据备份到页面文件中。 </p>
<h3 id="回收时机"><a href="#回收时机" class="headerlink" title="回收时机"></a>回收时机</h3><p>当满足下列条件之一时，进行垃圾回收：</p>
<ol>
<li><p>系统具有较低的物理内存。 这可以通过 OS 的低内存通知或主机指示的低内存来检测。</p>
</li>
<li><p>托管堆上已分配对象使用的内存超过可接受的阈值。 在该过程运行时不断调整该阈值。</p>
</li>
<li><p>调用 GC.Collect 方法。 几乎在所有情况下，您都不必调用此方法，因为垃圾收集器会持续运行。 此方法主要用于特别的情况和测试。</p>
</li>
</ol>
<h3 id="托管堆"><a href="#托管堆" class="headerlink" title="托管堆"></a>托管堆</h3><p>垃圾回收器由 CLR 初始化后，它会分配一段内存来存储和管理对象。 此内存称为托管堆，而不是操作系统中的本机堆。</p>
<p>每个托管进程都有一个托管堆。 进程中的所有线程都为同一堆上的对象分配内存。</p>
<p>为了保留内存，垃圾收集器调用 Win32 VirtualAlloc 函数，并为托管应用程序一次保留一段内存。 垃圾收集器还根据需要保留一片内存片段，并通过调用 Win32 VirtualFree 函数将此内存片段释放回操作系统（在清除对象之后）。</p>
<p>堆上分配的对象越少，垃圾收集器的工作就越少。在分配对象时，请不要使用超出需要的整数值（2的N次方），例如在只需要15个字节时分配32个字节的数组。</p>
<p>当触发垃圾收集时，垃圾收集器将回收垃圾对象占用的内存。回收过程压缩活动对象，使它们移动到一起，并移除垃圾区域，从而使堆更小。这可确保分配在一起的对象在托管堆上保持一致，以保留其位置。</p>
<p>垃圾收集的侵入性（频率和持续时间）由已分配的内存和托管堆上的剩余内存量决定。</p>
<p>堆可以被认为是两个堆的堆积：大对象堆和小对象堆。</p>
<p>大对象堆包含85,000字节或更大的非常大的对象。大对象堆上的对象通常是数组。实例对象很少是非常大的。</p>
<h3 id="垃圾回收的代际"><a href="#垃圾回收的代际" class="headerlink" title="垃圾回收的代际"></a>垃圾回收的代际</h3><p>堆被组织成3代，因此它可以处理长寿命和短寿命的对象。垃圾收集主要发生在回收通常仅占用堆的一小部分的短期对象时。堆上有三代对象：</p>
<ol>
<li><p>第0代。这是最年轻的一代，包含短期对象。典型的短期对象是函数内的临时变量。垃圾收集在这一代中最常发生。<br>新分配的对象一般会形成0代对象，除非它们是大对象，在这种情况下它们在第2代集合中的大对象堆上。<br>大多数对象在第0代被回收用于垃圾收集，并且不能存活到下一代。</p>
</li>
<li><p>第1代。此代包含短期对象，并用作短期对象和长期对象之间的缓冲区。</p>
</li>
<li><p>第2代。这一代包含长寿命对象。长期对象的一个​​典型例子是服务器应用程序中的对象，其中包含在进程持续时间内处于活动状态的静态数据。</p>
</li>
</ol>
<p>在条件允许的情况下，第n代回收，就意味着收集那一代及其所有比它年轻代的对象。所以第2代垃圾回收也称为完全垃圾回收（Full GC），因为它回收了所有代中的所有对象（即托管堆中的所有对象）。</p>
<h4 id="对象存活以及代际提升"><a href="#对象存活以及代际提升" class="headerlink" title="对象存活以及代际提升"></a>对象存活以及代际提升</h4><p>垃圾收集中未回收的对象称为幸存者，并被提升为下一代。 在第0代垃圾收集中存活的对象被提升为第1代; 在第1代垃圾收集中存活的对象被提升为第2代; 在第2代垃圾收集中存活的对象仍然存在。</p>
<p>当垃圾收集器在一代中检测到存活率很高时，它会增加该代的分配阈值，因此下一个集合会获得相当大的回收内存大小。 CLR 会不断平衡：不让应用程序占用内存太大和不让垃圾收集花费太多时间。</p>
<h4 id="Ephemeral-generations-and-segments-短暂代和内存片段"><a href="#Ephemeral-generations-and-segments-短暂代和内存片段" class="headerlink" title="Ephemeral generations and segments 短暂代和内存片段"></a><a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals#ephemeral-generations-and-segments" target="_blank" rel="noopener">Ephemeral generations and segments 短暂代和内存片段</a></h4><h3 id="垃圾回收时发生了什么？"><a href="#垃圾回收时发生了什么？" class="headerlink" title="垃圾回收时发生了什么？"></a>垃圾回收时发生了什么？</h3><p>垃圾收集包含以下阶段：</p>
<ol>
<li><p>标记阶段，用于查找并创建所有活动对象的列表。</p>
</li>
<li><p>重定位阶段，用于更新对将要压缩的对象的引用。</p>
</li>
<li><p>压缩阶段，回收死亡物体占据的空间并压缩幸存的物体。 压缩阶段将在垃圾收集中幸存的对象移动到段的较旧端。<br>由于第2代集合可以占用多个内存段，因此可以将升级为第2代的对象移动到较旧的内存段中。 第1代和第2代幸存者都可以移动到不同的部分，因为它们被提升到第2代。<br>通常，大型对象堆不会被压缩，因为复制大型对象会降低性能。 但是，从.NET Framework 4.5.1开始，您可以使用 GCSettings.LargeObjectHeapCompactionMode 属性按需压缩大对象堆。</p>
</li>
</ol>
<p>垃圾收集器使用以下信息来确定对象是否处于活动状态：</p>
<ol>
<li><p>堆栈根。 堆栈变量由 just-in-time (JIT) compiler 和 stack walker 提供。</p>
</li>
<li><p>垃圾回收句柄。 指向托管对象的句柄，可以由用户代码或公共语言运行库分配。</p>
</li>
<li><p>静态数据。 应用程序域中的静态对象，可以引用其他对象。 每个应用程序域都会跟踪其静态对象。</p>
</li>
</ol>
<p>在垃圾收集开始之前，除了触发垃圾收集的线程之外，所有托管线程都将被挂起。</p>
<p>下图显示了一个触发垃圾收集并导致其他线程被挂起的线程。</p>
<p><img src="/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/gc-triggered.png" alt="执行时序" title="触发垃圾回收"></p>
<h3 id="对象的-Finalize"><a href="#对象的-Finalize" class="headerlink" title="对象的 Finalize"></a>对象的 Finalize</h3><p>当发现可终结对象时，它的 Finalizer 将被放入队列中以便执行其清理操作，但对象本身将被提升为下一代。 因此，您必须等到该生成上发生的下一个垃圾收集（不一定是下一个垃圾收集）来确定该对象是否已被回收。</p>
<h3 id="Workstation-and-server-garbage-collection"><a href="#Workstation-and-server-garbage-collection" class="headerlink" title="Workstation and server garbage collection"></a><a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals#workstation-and-server-garbage-collection" target="_blank" rel="noopener">Workstation and server garbage collection</a></h3><h3 id="Concurrent-garbage-collectio"><a href="#Concurrent-garbage-collectio" class="headerlink" title="Concurrent garbage collectio"></a><a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals#concurrent-garbage-collection" target="_blank" rel="noopener">Concurrent garbage collectio</a></h3><h3 id="Background-workstation-garbage-collection"><a href="#Background-workstation-garbage-collection" class="headerlink" title="Background workstation garbage collection"></a><a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals#background-workstation-garbage-collection" target="_blank" rel="noopener">Background workstation garbage collection</a></h3><h3 id="Background-server-garbage-collection"><a href="#Background-server-garbage-collection" class="headerlink" title="Background server garbage collection"></a><a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals#background-server-garbage-collection" target="_blank" rel="noopener">Background server garbage collection</a></h3><h2 id="大对象堆（large-object-heap）"><a href="#大对象堆（large-object-heap）" class="headerlink" title="大对象堆（large object heap）"></a>大对象堆（large object heap）</h2><p>.NET 垃圾收集器（GC）将对象划分为小型和大型对象。 当一个对象很大时，它的一些属性变得比小对象更重要。 例如，压缩它(将它复制到堆中其他地方的内存中)的代价可能很高。 因此，.NET垃圾收集器将大对象放在大对象堆（LOH）上。 在本主题中，我们将深入研究大对象堆。 我们将讨论什么使对象成为大对象，如何收集这些大对象，以及大对象强加什么样的性能。</p>
<p><strong>注意：本主题讨论仅在 Windows 系统上运行的 .NET Framework 和 .NET Core 中的大对象堆。 它不包括在其他平台上运行 .NET 实现的 LOH 。</strong></p>
<h3 id="对象如何在大对象堆上被回收以及-GC-如何处理它们"><a href="#对象如何在大对象堆上被回收以及-GC-如何处理它们" class="headerlink" title="对象如何在大对象堆上被回收以及 GC 如何处理它们"></a>对象如何在大对象堆上被回收以及 GC 如何处理它们</h3><p>如果一个对象大于或等于85,000字节，则认为它是一个大对象。此数字是根据性能来确定的。当对象分配请求是85,000或更多字节时，运行时将它分配给大对象堆。</p>
<p>要理解这意味着什么，则需要回顾一下关于 .NET GC 的一些基础知识。</p>
<p>.NET垃圾收集器是一代分类收集器。它有三代：第0代，第1代和第2代。拥有3代的原因是，在经过良好调整的应用程序中，大多数对象都在 gen0 中死亡。例如，在服务器应用程序中，与请求关联的分配应在请求完成后消失。正在进行的分配请求将使其成为 gen1 并在那里死亡。从本质上讲，gen1 充当年轻对象区域和长寿命对象区域之间的缓冲区。</p>
<p>小对象总是在第0代中分配，并且根据其生命周期，可以被提升为第1代或第2代。大型对象始终在第2代中分配。</p>
<p>物理上，对象存在于托管堆中。托管堆是 GC 通过托管代码调用 VirtualAlloc 函数从 OS 申请的一块内存。加载 CLR 时，GC会分配两个初始堆段：一个用于小对象（小对象堆，或SOH），另一个用于大对象（大对象堆）。</p>
<p>用户代码只能在第0代（小对象）或LOH（大对象）中分配。只有GC可以在第1代（通过促进第0代幸存者）和第2代（通过提升第1代和第2代的幸存者的代际）“分配”对象。</p>
<p>触发垃圾收集时，GC 会跟踪活动对象并压缩它们。但由于压缩大对象成本很高，所以GC 先扫描 LOH 后，并利用临近 LOH 的垃圾对象，回收他们然他们变成 LOH 空闲空间，来满足大对象分配请求。</p>
<p>.NET Core和.NET Framework（从.NET Framework 4.5.1开始）包括GCSettings.LargeObjectHeapCompactionMode 属性，该属性允许用户指定在下一个完全阻塞 GC 期间应该压缩 LOH。并且在未来，.NET可能会决定自动压缩 LOH。这意味着，如果您分配大型对象并希望确保它们不移动，您仍应将它们固定。</p>
<p>图1说明了 GC 在 Obj1 和 Obj3 已经变为垃圾对象后进行 0代 GC 之后形成第1代的情况。还有 GC 在 Obj2 和 Obj5 已经变为垃圾对象后，执行 1代 GC 之后形成第2代的情况。 请注意，此图和以下图仅用于说明目的; 它们包含的对象非常少，可以更好地显示堆上发生的情况。 实际上，GC中通常涉及更多对象。</p>
<p><img src="/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/loh-figure-1.jpg" alt="图1" title="图1"></p>
<p>图2显示了在 Obj1 和 Obj2 变为垃圾对象，进行 2代 GC。</p>
<p><img src="/my_blog/2018/08/10/DotNet-Guide-Garbage-Collection/loh-figure-2.jpg" alt="图2" title="图2"></p>
<p>如果没有足够的可用空间来容纳大对象分配请求，则GC首先尝试从OS获取更多内存段。如果失败，它会触发第2代GC，来释放一些空间。</p>
<h3 id="LOH-性能影响"><a href="#LOH-性能影响" class="headerlink" title="LOH 性能影响"></a>LOH 性能影响</h3><p>大对象堆上的分配以下列方式影响性能。</p>
<ol>
<li><p>分配成本。<br>CLR 确保清除它给出的每个新对象的内存。这意味着大对象的分配成本完全由内存清除控制（除非它触发GC）。如果清除一个字节需要2个周期，则清除最小的大对象需要170,000个周期。在2GHz机器上清除16MB对象的内存大约需要16ms。这是一个相当大的成本。</p>
</li>
<li><p>回收成本。<br>如果由于超过 LOH 大小而触发 2代回收，则在 GC 之后第2代不一定会小得多。如果第2代的数据不多，则影响最小。但是，如果第2代很大，如果触发了许多第2代GC，则可能会导致性能问题。如果许多大型物体是在非常临时的基础上分配的，并且您的SOH很大，那么您可能会花太多时间去做GC。另外，如果你继续分配和释放非常大的对象，分配成本确实会增加。</p>
</li>
<li><p>具有引用类型的数组元素。<br>LOH上的非常大的对象通常是数组（很少有实例对象非常大）。如果数组的元素是引用丰富的，则如果元素不是引用丰富的，则会产生不存在的成本。如果元素不包含任何引用，则垃圾收集器根本不需要遍历该数组。</p>
</li>
</ol>
<p>在这三个因素中，前两个通常比第三个更重要。 因此，我们建议您分配一个可以重用的大型对象池，而不是分配临时对象。</p>
<h2 id="垃圾回收的性能"><a href="#垃圾回收的性能" class="headerlink" title="垃圾回收的性能"></a>垃圾回收的性能</h2><p>本节内容为垃圾回收和内存使用相关的问题。 它解决了与托管堆相关的问题，并解释了如何最小化垃圾回收对应用程序的影响。</p>
<h3 id="性能分析工具"><a href="#性能分析工具" class="headerlink" title="性能分析工具"></a>性能分析工具</h3><h4 id="Memory-Performance-Counters"><a href="#Memory-Performance-Counters" class="headerlink" title="Memory Performance Counters"></a>Memory Performance Counters</h4><p>您可以使用性能计数器来收集性能数据。 有关说明，请参阅 <a href="https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/runtime-profiling" target="_blank" rel="noopener">Runtime Profiling</a>。 .NET CLR 内存类别的性能计数器（<a href="https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/performance-counters" target="_blank" rel="noopener">Performance Counters in the .NET Framework</a>）提供有关垃圾收集器的信息。</p>
<h4 id="Debugging-with-SOS"><a href="#Debugging-with-SOS" class="headerlink" title="Debugging with SOS"></a>Debugging with SOS</h4><p>您可以使用 <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/index" target="_blank" rel="noopener">Windows Debugger (WinDbg)</a> 检查托管堆上的对象。</p>
<p>要安装WinDbg，请从<a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools" target="_blank" rel="noopener">下载Windows调试工具</a>页面安装Windows调试工具。</p>
<h4 id="Garbage-Collection-ETW-Events"><a href="#Garbage-Collection-ETW-Events" class="headerlink" title="Garbage Collection ETW Events"></a>Garbage Collection ETW Events</h4><p>Event tracing for Windows (ETW) 是一个跟踪系统，它补充了 .NET Framework 提供的分析和调试支持。 从.NET Framework 4 开始，垃圾收集 ETW 事件从统计角度捕获用于分析托管堆的有用信息。 例如，垃圾收集即将发生时引发的 GCStart_V1 事件提供以下信息：</p>
<ol>
<li><p>正在收集哪一代对象。</p>
</li>
<li><p>什么引发了垃圾收集。</p>
</li>
<li><p>垃圾收集的类型（并发或非并发）。</p>
</li>
</ol>
<p>ETW 事件日志记录是高效的，不会掩盖与垃圾收集相关的任何性能问题。 进程可以提供自己的事件。 记录时，可以关联应用程序的事件和垃圾回收事件，以确定堆问题的发生方式和时间。 例如，服务器应用程序可以在客户端请求的开始和结束时的事件。</p>
<h4 id="The-Profiling-API"><a href="#The-Profiling-API" class="headerlink" title="The Profiling API"></a>The Profiling API</h4><p>公共语言运行时（CLR）分析界面提供有关在垃圾回收期间受影响的对象的详细信息。 垃圾收集开始和结束时可以通知探查器。 它可以提供有关托管堆上的对象的报告，包括每一代中对象的标识。 有关更多信息，请参阅<a href="https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/profiling-overview" target="_blank" rel="noopener">性能分析概述</a>。</p>
<p>Profilers 可以提供全面的信息。 但是，复杂的分析器可能会修改应用程序的行为。</p>
<h4 id="Application-Domain-Resource-Monitoring"><a href="#Application-Domain-Resource-Monitoring" class="headerlink" title="Application Domain Resource Monitoring"></a>Application Domain Resource Monitoring</h4><p>从.NET Framework 4开始，应用程序域资源监视（ARM）使主机能够按应用程序域监视CPU和内存使用情况。 有关更多信息，请参阅<a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/app-domain-resource-monitoring" target="_blank" rel="noopener">应用程序域资源监视</a>。</p>
<h2 id="执行回收操作的时机"><a href="#执行回收操作的时机" class="headerlink" title="执行回收操作的时机"></a>执行回收操作的时机</h2><p>在大多数情况下，垃圾收集器可以确定执行回收的最佳时间，您应该让它独立运行。强制收集可能会提高应用程序性能的情况很少见。只有在这些情况下，您可以使用 GC.Collect 方法强制垃圾回收来触发垃圾回收。</p>
<p>只有当应用程序代码中的特定点，进行垃圾回收能让使用的内存量显着减少时，才使用 GC.Collect 方法。例如，如果您的应用程序使用具有多个控件的复杂对话框，则在关闭对话框时调用 Collect 可以通过立即回收对话框使用的内存来提高性能。确保您的应用程序不会过于频繁地导致垃圾收集，因为如果垃圾收集器试图在非最佳时间回收对象，则会降低性能。您可以向 Collect 方法提供GCCollectionMode.Optimized 枚举值，以便仅在能回收操作有效时回收。</p>
<h3 id="回收模式"><a href="#回收模式" class="headerlink" title="回收模式"></a>回收模式</h3><p>GCCollectionMode.Default : 采用当前.net版本的默认值。</p>
<p>GCCollectionMode.Force : 强制垃圾回收立即执行。 这相当于调用 GC.Collect。 它导致所有世代的完全阻塞回收。</p>
<p>GCCollectionMode.Optimized : 使垃圾收集器确定当前是否是回收对象的最佳时间。<br>垃圾收集器可以计算当前时间点进行回收的效率，如果效率不够高，它将直接返回而不回收对象。</p>
<h2 id="延迟模式"><a href="#延迟模式" class="headerlink" title="延迟模式"></a>延迟模式</h2><p>要回收对象，垃圾回收器必须停止应用程序中的所有正在执行的线程。 在某些情况下，例如当应用程序检索数据或显示内容时，可能会在关键时刻进行完整的垃圾收集并阻碍性能。 您可以通过将 GCSettings.LatencyMode 属性设置为 System.Runtime.GCLatencyMode 值之一来调整垃圾收集器的侵入性。</p>
<p>延迟是指垃圾收集器侵入您的应用程序的时间。在低延迟期间，垃圾收集器在回收对象时更加保守且侵入性更小。 System.Runtime.GCLatencyMode 枚举提供两个低延迟设置：</p>
<ol>
<li><p>LowLatency禁止 2代回收，仅执行第0代和第1代集合。它只能在短时间内使用。在较长时间内，如果系统处于内存压力下，垃圾收集器将触发收集，这可能会暂停应用程序并中断对时间要求严格的操作。此设置仅适用于 WorkStation 垃圾回收。</p>
</li>
<li><p>SustainedLowLatency 不进行前台的 2代回收，只执行第0代，第1代和第2代后台回收。它可以长时间使用，可用于工作站和服务器垃圾收集。如果禁用并发垃圾回收，则无法使用此设置。</p>
</li>
</ol>
<h2 id="弱引用"><a href="#弱引用" class="headerlink" title="弱引用"></a>弱引用</h2><p>当应用程序的代码可以访问该对象时，垃圾收集器无法收集应用程序正在使用的对象。该应用程序对该对象有强引用。</p>
<p>弱引用允许垃圾收集器收集对象，同时仍允许应用程序访问对象。弱引用仅在不存在强引用时收集对象之前的不确定时间内有效。当您使用弱引用时，应用程序仍然可以获得对该对象的强引用，从而阻止其被收集。但是，在重新建立强引用之前，始终存在垃圾收集器首先到达对象的风险。</p>
<p>弱引用对于被垃圾回收回收后可以轻松地重新创建的，而且使用大量内存的对象很有用。</p>
<p>假设Windows窗体应用程序中的树视图向用户显示复杂的分层选项选项。如果底层数据很大，当用户涉及应用程序中的其他内容时，将树保留在内存中效率很低。</p>
<p>当用户切换到应用程序的另一部分时，您可以使用WeakReference类创建对树的弱引用并销毁所有强引用。当用户切换回树时，应用程序尝试获取对树的强引用，如果成功，则避免重构树。</p>
<p>要使用对象建立弱引用，可以使用要跟踪的对象的实例创建WeakReference。然后，将Target属性设置为该对象，并将对象的原始引用设置为null。有关代码示例，请参阅类库中的<a href="https://docs.microsoft.com/en-us/dotnet/api/system.weakreference" target="_blank" rel="noopener">WeakReference</a>。</p>
<h3 id="短期和长期弱引用"><a href="#短期和长期弱引用" class="headerlink" title="短期和长期弱引用"></a>短期和长期弱引用</h3><p>您可以创建一个简短的弱引用或一个长弱引用：</p>
<ol>
<li><p>短期：<br>当垃圾收集回收对象时，短弱引用的目标变为 null。弱引用本身就是一个托管对象，并且像任何其他托管对象一样受垃圾收集的影响。一个简短的弱引用是 WeakReference 的默认构造函数。</p>
</li>
<li><p>长期：<br>调用对象的 Finalize 方法后，将保留长期的弱引用。这允许重新创建对象，但对象的状态仍然是不可预测的。要使用长引用，请在 WeakReference 构造函数中指定 true。</p>
</li>
</ol>
<p>如果对象的类型没有Finalize方法，则应用短弱引用功能，并且弱引用仅在收集目标之前有效，这可以在终结器运行后的任何时间发生。</p>
<p>要建立强引用并再次使用该对象，请将 WeakReference 的 Target 属性强制转换为对象的类型。如果 Target 属性返回 null ，则该对象已被回收;否则，您可以继续使用该对象，因为该应用程序已重新获得对它的强引用。</p>
<h3 id="使用弱引用的指南"><a href="#使用弱引用的指南" class="headerlink" title="使用弱引用的指南"></a>使用弱引用的指南</h3><p>仅在必要时使用长期弱引用，因为在完成后对象的状态是不可预测的。</p>
<p>避免对小对象使用弱引用，因为指针本身可能和对象一样大或更大。</p>
<p>避免使用弱引用作为内存管理问题的自动解决方案。 相反，开发一个有效的缓存策略来处理应用程序的对象。</p>
<h2 id="Optimization-for-Shared-Web-Hosting"><a href="#Optimization-for-Shared-Web-Hosting" class="headerlink" title="Optimization for Shared Web Hosting"></a><a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/optimization-for-shared-web-hosting" target="_blank" rel="noopener">Optimization for Shared Web Hosting</a></h2>
          
        
      
    </div>

    

    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://sdfsd23few23.gitee.io/my_blog/my_blog/2018/08/10/DotNet-Guide-Memory-Management/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Raymond">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/my_blog/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Raymond's Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/my_blog/2018/08/10/DotNet-Guide-Memory-Management/" itemprop="url">
                  Memory Management and Garbage Collection in .NET - 1
                </a>
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Veröffentlicht am</span>
              

              
                
              

              <time title="Post created: 2018-08-10 20:45:33" itemprop="dateCreated datePublished" datetime="2018-08-10T20:45:33+08:00">2018-08-10</time>
            

            
              

              
                
                <span class="post-meta-divider">|</span>
                

                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                
                  <span class="post-meta-item-text">Edited on</span>
                
                <time title="Updated at: 2018-08-11 00:00:16" itemprop="dateModified" datetime="2018-08-11T00:00:16+08:00">2018-08-11</time>
              
            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">in</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/my_blog/categories/Coding/" itemprop="url" rel="index"><span itemprop="name">Coding</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p>#.NET 中的内存管理和垃圾回收机制 (1) - 内存管理</p>
<p>本文参考来源：<a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/memory-management-and-gc" target="_blank" rel="noopener">Memory Management and Garbage Collection in .NET</a> </p>
<h2 id="内存管理-清理非托管资源"><a href="#内存管理-清理非托管资源" class="headerlink" title="内存管理 - 清理非托管资源"></a>内存管理 - 清理非托管资源</h2><p>对于应用程序创建的大多数对象，一般情况下可以依靠.NET的垃圾收集器来处理内存管理。 但是，对于包含非托管资源的对象，必须在创建并使用后，<strong>显式</strong>的释放其中的非托管的资源。</p>
<p>最常见的非托管资源是操作系统的资源，例如文件，窗口，网络连接或数据库连接。</p>
<p>虽然垃圾收集器能够跟踪封装非托管资源的对象的生命周期，但它不知道如何释放和清理非托管资源。</p>
<p>对于包含非托管资源的对象，你应当：</p>
<ol>
<li><p>实现 <a href="https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/dispose-pattern" target="_blank" rel="noopener">dispose pattern</a>：即实现 <a href="https://docs.microsoft.com/en-us/dotnet/api/system.idisposable.dispose" target="_blank" rel="noopener">IDisposable</a> 接口，并在接口中实现对非托管对象的显式释放。当你的类的调用方调用类的Dispose方法时，非托管资源即可立即释放。</p>
</li>
<li><p>当你的类的调用方没有调用Dispose方法时，你需要通过下面两种方法（二选一），释放非托管资源：</p>
<ul>
<li><p>使用 safe handle 来封装非托管资源，这是推荐的方法。 safe handle 派生自 <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.safehandle" target="_blank" rel="noopener">System.Runtime.InteropServices.SafeHandle</a> 类，并包含一个强大的Finalize方法。 当使用 safe handle 时，只需实现 IDisposable 接口并在 IDisposable.Dispose 实现中调用 safe handle 的 Dispose 方法。 如果没有调用 Dispose 方法，则垃圾收集器会自动调用 safe handle 的终结器。</p>
</li>
<li><p>重写 Object.Finalize 方法。 当类型的调用方没有调用 IDisposable.Dispose 时，Finalize 方法可以非确定的释放非托管资源（根据GC）。 但是，由于对象的 Finalize 是一个复杂且容易出错的操作，因此我们建议使用 safe handle 而不是提供自己的 Finalize 方法。</p>
</li>
</ul>
</li>
</ol>
<p>当正确实现Dispose方法时，safe handle 的 Finalize 方法或重写的 Object.Finalize 方法将成为在未调用 Dispose 方法时的安全措施。</p>
<h3 id="实现-Dispose-方法"><a href="#实现-Dispose-方法" class="headerlink" title="实现 Dispose 方法"></a>实现 Dispose 方法</h3><p>实现 Dispose 的两种方法在前面的章节已经介绍过了，需要注意的是为了确保始终适当地清理资源，Dispose方法应该可以多次调用，而不会抛出异常。</p>
<p><strong>注意</strong>：当 Finalize 方法执行时间过长时，在 Dispose 方法结束时调用 GC.KeepAlive() 方法来确保GC回收内存时，Dispose 方法已经执行完毕。</p>
<h4 id="Dispose-和-Dispose-Boolean"><a href="#Dispose-和-Dispose-Boolean" class="headerlink" title="Dispose() 和 Dispose(Boolean)"></a>Dispose() 和 Dispose(Boolean)</h4><p>下面是VS自动实现的 IDispoable 接口：</p>
<pre><code>class Class1 : IDisposable
{

    #region IDisposable Support
    private bool disposedValue = false; // 要检测冗余调用

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: 释放托管状态(托管对象)。
            }

            // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
            // TODO: 将大型字段设置为 null。

            disposedValue = true;
        }
    }

    // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
    // ~Class1() {
    //   // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
    //   Dispose(false); //传递 False 来阻止 Finalize 尝试释放可能已经回收的托管资源
    // }

    // 添加此代码以正确实现可处置模式。
    public void Dispose()
    {
        // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
        Dispose(true);
        // TODO: 如果在以上内容中替代了终结器，则取消注释以下行。
        // GC.SuppressFinalize(this);
    }
    #endregion
}
</code></pre><h4 id="使用-safe-handles"><a href="#使用-safe-handles" class="headerlink" title="使用 safe handles"></a>使用 safe handles</h4><p>Microsoft.Win32.SafeHandles 命名空间提供了一组派生自 SafeHandle 的类。 如果找不到适合的非托管资源的类，你可以实现自己的SafeHandle子类。</p>
<ol>
<li><p>The SafeFileHandle, SafeMemoryMappedFileHandle, and SafePipeHandle class.</p>
</li>
<li><p>The SafeMemoryMappedViewHandle class.</p>
</li>
<li><p>The SafeNCryptKeyHandle, SafeNCryptProviderHandle, and SafeNCryptSecretHandle classes.</p>
</li>
<li><p>The SafeRegistryHandle class.</p>
</li>
<li><p>The SafeWaitHandle class.</p>
</li>
</ol>
<p>以下示例定义了一个 DisposableResource 类，它使用 SafeFileHandle 来包装表示打开文件的 Stream 对象。 DisposableResource 方法还包括一个属性 Size，它返回文件流中的总字节数。</p>
<pre><code>using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;

public class DisposableStreamResource : IDisposable
{
    // Define constants.
    protected const uint GENERIC_READ = 0x80000000;
    protected const uint FILE_SHARE_READ = 0x00000001;
    protected const uint OPEN_EXISTING = 3;
    protected const uint FILE_ATTRIBUTE_NORMAL = 0x80;
    protected IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
    private const int INVALID_FILE_SIZE = unchecked((int) 0xFFFFFFFF);

    // Define Windows APIs.
    [DllImport(&quot;kernel32.dll&quot;, EntryPoint = &quot;CreateFileW&quot;, CharSet = CharSet.Unicode)]
    protected static extern IntPtr CreateFile (
                                    string lpFileName, uint dwDesiredAccess, 
                                    uint dwShareMode, IntPtr lpSecurityAttributes, 
                                    uint dwCreationDisposition, uint dwFlagsAndAttributes, 
                                    IntPtr hTemplateFile);

    [DllImport(&quot;kernel32.dll&quot;)]
    private static extern int GetFileSize(SafeFileHandle hFile, out int lpFileSizeHigh);

    // Define locals.
    private bool disposed = false;
    private SafeFileHandle safeHandle; 
    private long bufferSize;
    private int upperWord;

    public DisposableStreamResource(string filename)
    {
        if (filename == null)
            throw new ArgumentNullException(&quot;The filename cannot be null.&quot;);
        else if (filename == &quot;&quot;)
            throw new ArgumentException(&quot;The filename cannot be an empty string.&quot;);

        IntPtr handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
                                    IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                                    IntPtr.Zero);
        if (handle != INVALID_HANDLE_VALUE)
            safeHandle = new SafeFileHandle(handle, true);
        else
            throw new FileNotFoundException(String.Format(&quot;Cannot open &apos;{0}&apos;&quot;, filename));

        // Get file size.
        bufferSize = GetFileSize(safeHandle, out upperWord); 
        if (bufferSize == INVALID_FILE_SIZE)
            bufferSize = -1;
        else if (upperWord &gt; 0) 
            bufferSize = (((long)upperWord) &lt;&lt; 32) + bufferSize;
    }

    public long Size 
    { get { return bufferSize; } }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }           

    protected virtual void Dispose(bool disposing)
    {
        if (disposed) return;

        // Dispose of managed resources here.
        if (disposing)
            safeHandle.Dispose();

        // Dispose of any unmanaged resources not wrapped in safe handles.

        disposed = true;
    }  
}
</code></pre><h3 id="使用实现了-IDisposable-接口的对象"><a href="#使用实现了-IDisposable-接口的对象" class="headerlink" title="使用实现了 IDisposable 接口的对象"></a>使用实现了 IDisposable 接口的对象</h3><p>当您完成使用实现 IDisposable 的对象时，您应该调用该对象的 Disposable.Dispose 实现。 你可以通过以下两种方式之一完成此操作：</p>
<ol>
<li><p>使用 using 语句。</p>
</li>
<li><p>通过实现 try / finally 块。</p>
</li>
</ol>
<h4 id="using-语句"><a href="#using-语句" class="headerlink" title="using 语句"></a>using 语句</h4><p>C＃ 中的 using 语句简化了必须编写以创建和清理对象的代码。 using 语句获取一个或多个资源，执行您指定的语句，并自动处理该对象。 </p>
<p>但是，using语句仅对在构造它们的方法范围内使用的对象有用。</p>
<p>以下示例使用using语句创建和释放System.IO.StreamReader对象。</p>
<p>using System;<br>using System.IO;</p>
<pre><code>public class Example
{
    public static void Main()
    {
        Char[] buffer = new Char[50];
        using (StreamReader s = new StreamReader(&quot;File1.txt&quot;)) {
            int charsRead = 0;
            while (s.Peek() != -1) {
                charsRead = s.Read(buffer, 0, buffer.Length);
                //
                // Process characters read.
                //   
            }
            s.Close();    
        }

    }
}
</code></pre><p>C＃using 语句还允许您在单个语句中获取多个资源，这在内部等效于嵌套的 using 语句。 以下示例实例化两个 StreamReader 对象以读取两个不同文件的内容。</p>
<p>using System;<br>using System.IO;</p>
<pre><code>public class Example
{
    public static void Main()
    {
        Char[] buffer1 = new Char[50], buffer2 = new Char[50];

        using (StreamReader version1 = new StreamReader(&quot;file1.txt&quot;),
                            version2 = new StreamReader(&quot;file2.txt&quot;)) {
            int charsRead1, charsRead2 = 0;
            while (version1.Peek() != -1 &amp;&amp; version2.Peek() != -1) {
                charsRead1 = version1.Read(buffer1, 0, buffer1.Length);
                charsRead2 = version2.Read(buffer2, 0, buffer2.Length);
                //
                // Process characters read.
                //
            }
            version1.Close();
            version2.Close();
        }
    }
}
</code></pre><h4 id="try-finally-代码块"><a href="#try-finally-代码块" class="headerlink" title="try / finally 代码块"></a>try / finally 代码块</h4><p>当 C＃ 编译器遇到using语句时，它会生成中间语言（IL），它等效于以下显式包含try / finally块的代码。</p>
<pre><code>using System;
using System.IO;

public class Example
{
    public static void Main()
    {
        Char[] buffer = new Char[50];
        {
            StreamReader s = new StreamReader(&quot;File1.txt&quot;); 
            try {
                int charsRead = 0;
                while (s.Peek() != -1) {
                charsRead = s.Read(buffer, 0, buffer.Length);
                //
                // Process characters read.
                //   
                }
                s.Close();
            }
            finally {
                if (s != null)
                ((IDisposable)s).Dispose();     
            }       
        }
    }
}
</code></pre>
          
        
      
    </div>

    

    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://sdfsd23few23.gitee.io/my_blog/my_blog/2018/06/24/DotNet-Guide-Threading/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Raymond">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/my_blog/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Raymond's Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/my_blog/2018/06/24/DotNet-Guide-Threading/" itemprop="url">
                  Parallel Processing, Concurrency, and Async Programming in .NET - 1
                </a>
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Veröffentlicht am</span>
              

              
                
              

              <time title="Post created: 2018-06-24 18:14:09" itemprop="dateCreated datePublished" datetime="2018-06-24T18:14:09+08:00">2018-06-24</time>
            

            
              

              
                
                <span class="post-meta-divider">|</span>
                

                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                
                  <span class="post-meta-item-text">Edited on</span>
                
                <time title="Updated at: 2018-06-27 21:10:47" itemprop="dateModified" datetime="2018-06-27T21:10:47+08:00">2018-06-27</time>
              
            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">in</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/my_blog/categories/Coding/" itemprop="url" rel="index"><span itemprop="name">Coding</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h1 id="NET-中的异步-多线程-并发处理-1-线程"><a href="#NET-中的异步-多线程-并发处理-1-线程" class="headerlink" title=".NET 中的异步/多线程/并发处理(1) - 线程"></a>.NET 中的异步/多线程/并发处理(1) - 线程</h1><p>本文核心：</p>
<ol>
<li>如何使用户交互界面具有良好的响应？</li>
<li>如何最大化利用CPU的性能？</li>
</ol>
<p>针对上面两个问题，根据 .NET Guide 中的 <a href="https://docs.microsoft.com/en-us/dotnet/standard/parallel-processing-and-concurrency" target="_blank" rel="noopener">Parallel Processing, Concurrency, and Async Programming in .NET</a> 的内容，进一步深入理解 .NET 的下面三种机制：</p>
<ol>
<li>异步编程</li>
<li>并行处理</li>
<li>多线程与线程同步机制。</li>
</ol>
<h2 id="线程"><a href="#线程" class="headerlink" title="线程"></a>线程</h2><h3 id="1-托管线程基础"><a href="#1-托管线程基础" class="headerlink" title="1. 托管线程基础"></a>1. 托管线程基础</h3><h4 id="Threads-and-Threading"><a href="#Threads-and-Threading" class="headerlink" title="Threads and Threading"></a>Threads and Threading</h4><p>操作系统使用进程来分离正在执行的不同应用程序。线程是操作系统分配处理器时间的基本单位，并且多个线程可以在该进程内执行。每个线程维护自己的：异常处理、调度优先级以及一组用于保存线程上下文的结构。线程上下文：存储在所属进程的地址空间中的，线程能够无缝恢复执行的所有信息（一组CPU寄存器和堆栈）。</p>
<p>.NET Framework进一步将操作系统进程细分为由System.AppDomain表示的称为应用程序域的轻量级托管子进程。一个或多个托管线程（由System.Threading.Thread表示）可以在同一个托管进程中的一个或任意数量的应用程序域中运行。尽管每个应用程序域都使用单个线程启动，但该应用程序域中的代码可以创建其他应用程序域和其他线程。其结果是托管线程可以在同一个托管进程中的应用程序域之间自由移动;但只有一个线程在多个应用程序域中移动。</p>
<p>支持抢先式多任务的操作系统可以同时执行多个进程中的多个线程。它通过在需要它的线程之间分配可用的处理器时间，为每个线程分配一个处理器时间片。当前正在执行的线程在其时间片消逝时挂起，另一个线程恢复运行。当系统从一个线程切换到另一个线程时，它保存抢先线程的线程上下文，并重新加载线程队列中下一个线程的已保存线程上下文。</p>
<p>时间片的长度取决于操作系统和处理器。由于每个时间片都很小，因此不管是一个处理器还是多处理器，多个线程看起来是同时执行的。但是实质上只有多处理器的系统实际上会有两个以上的线程同时执行，线程分布在可用的处理器中。</p>
<p>多线程的优缺点：使用尽可能少的线程，从而最大限度地减少操作系统资源的使用并提高性能。线程在设计应用程序时也需要考虑资源需求和潜在的冲突。资源需求如下：</p>
<ol>
<li>进程，AppDomain对象和线程所需的上下文信息，都消耗系统的内存。因此，可以创建的进程，AppDomain对象和线程的数量受可用内存的限制。 </li>
<li>跟踪大量的线程会消耗大量的处理器时间。如果线程太多，他们中的大多数不会取得重大进展。如果大多数当前线程都在一个进程中，则其他进程中的线程的调度次数会减少。</li>
<li>控制许多线程的代码执行非常复杂，并且可能成为许多错误的来源。 </li>
<li>销毁线程时需要注意会发生什么，而且要注意处理线程中的异常。 </li>
</ol>
<p>提供对资源的共享访问可能会产生冲突。为避免冲突，您必须同步或控制对共享资源的访问。如果无法正确同步访问（在相同或不同的应用程序域中），可能会导致问题，如死锁（其中两个线程停止响应，而每个线程都会等待另一个线程完成）以及竞争条件（由于意外而出现异常结果严重依赖两个事件的时间）。系统提供可用于协调多个线程之间资源共享的同步对象。减少线程数量可以更容易地同步资源。 </p>
<p>需要同步的资源包括：</p>
<ol>
<li>系统资源（如通信端口）。 </li>
<li>资源由多个进程共享（如文件句柄）。 </li>
<li>由多个线程访问的单个应用程序域（例如全局，静态和实例字段）的资源。</li>
</ol>
<p>一般来说，使用ThreadPool类是处理多个线程的最简单方法，用于处理相对较短的任务，这些任务不会阻止其他线程，并且不需要指定特定的任务调度。</p>
<p>但是，如果是以下几种原因，那么你可能需要自行创建线程：</p>
<ol>
<li>如果您需要一项任务来获得特定的优先级。 </li>
<li>如果您有一项任务可能会运行很长时间（并因此阻止其他任务）。 </li>
<li>如果您需要将线程放入单线程单元（所有ThreadPool线程都在多线程单元中）。 </li>
<li>如果您需要与该线程关联的稳定身份。例如，您应该使用专用线程来中止该线程，暂停或按名称发现它。 </li>
</ol>
<h4 id="线程数据同步"><a href="#线程数据同步" class="headerlink" title="线程数据同步"></a>线程数据同步</h4><p>.NET 提供了几种策略来同步对实例和静态成员的访问：</p>
<ol>
<li>同步代码区域。您可以对此类使用Monitor类或编译器支持来仅同步需要它的代码块，从而提高性能。 </li>
<li>手动同步。您可以使用.NET Framework类库提供的同步对象。请参阅同步基元概述，其中包括对Monitor类的讨论。 </li>
<li>同步的上下文。您可以使用SynchronizationAttribute为ContextBoundObject对象启用简单的自动同步。 System.Collections.Concurrent命名空间中的Collection类。这些类提供了内置的同步添加和删除操作。有关更多信息，请参阅<a href="https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/index" target="_blank" rel="noopener">Thread-Safe Collections</a>。</li>
</ol>
<h4 id="同步基元概述"><a href="#同步基元概述" class="headerlink" title="同步基元概述"></a>同步基元概述</h4><h5 id="Locking"><a href="#Locking" class="headerlink" title="Locking"></a>Locking</h5><p>独占锁：</p>
<ol>
<li>lock/Monitor：</li>
<li>Mutex/Mutexes：互斥</li>
<li>SpinLock：自旋锁，如果锁保持很短的时间，SpinLock可以提供比阻塞更好的性能。但是，如果锁持续数十个周期，SpinLock的性能与Monitor相当。</li>
</ol>
<p>非独占锁：不必是排他性的锁。允许有限数量的线程在并发访问资源通常很有用。Semaphores and reader-writer locks 旨在控制这种资源池式访问。</p>
<ol>
<li><a href="https://docs.microsoft.com/en-us/dotnet/standard/threading/overview-of-synchronization-primitives#readerwriterlock-class" target="_blank" rel="noopener">ReaderWriterLock Class</a></li>
<li><a href="https://docs.microsoft.com/en-us/dotnet/standard/threading/overview-of-synchronization-primitives#semaphore-class" target="_blank" rel="noopener">Semaphore Class</a></li>
</ol>
<h5 id="Signaling"><a href="#Signaling" class="headerlink" title="Signaling"></a>Signaling</h5><ol>
<li>Wait Handles</li>
<li>Event Wait Handles : AutoResetEvent 和 ManualResetEvent</li>
<li>Barrier : Barrier类提供了一种循环同步多个线程的方法，以便它们在同一个点处阻塞并等待所有其他线程完成。比如：当一个或多个线程在继续到算法的下一个阶段之前需要另一个线程的结果。</li>
</ol>
<p>下面是 Barrier 类的一个例子：</p>
<pre><code>// Create the Barrier object, and supply a post-phase delegate 
// to be invoked at the end of each phase.
Barrier barrier = new Barrier(2, (bar) =&gt; 
    {
        // Examine results from all threads, determine 
        // whether to continue, create inputs for next phase, etc. 
        if (someCondition)
            success = true;
    });       


// Define the work that each thread will perform. (Threads do not
// have to all execute the same method.)
void CrunchNumbers(int partitionNum)
{
    // Up to System.Int64.MaxValue phases are supported. We assume
    // in this code that the problem will be solved before that.
    while (success == false)
    {
        // Begin phase:
        // Process data here on each thread, and optionally
        // store results, for example:
        results[partitionNum] = ProcessData(data[partitionNum]);

        // End phase:
        // After all threads arrive,post-phase delegate
        // is invoked, then threads are unblocked. Overloads
        // accept a timeout value and/or CancellationToken.
        barrier.SignalAndWait();
    }
}

// Perform n tasks to run in in parallel. For simplicity
// all threads execute the same method in this example.
static void Main()
{
    var app = new BarrierDemo();
    Thread t1 = new Thread(() =&gt; app.CrunchNumbers(0));
    Thread t2 = new Thread(() =&gt; app.CrunchNumbers(1));
    t1.Start();
    t2.Start();

}
</code></pre><h5 id="Lightweight-Synchronization-Types"><a href="#Lightweight-Synchronization-Types" class="headerlink" title="Lightweight Synchronization Types"></a>Lightweight Synchronization Types</h5><p>从 .NET Framework 4 开始，您可以使用同步基元，通过尽可能避免昂贵地依赖Win32内核对象（如等待句柄）来提供快速性能。一般来说，当等待时间较短时，只有在尝试原始同步类型并发现不合格时，才应使用这些类型。轻量级类型不能用于需要跨进程通信的场景。 </p>
<ol>
<li>System.Threading.SemaphoreSlim是System.Threading.Semaphore的轻量级版本。 </li>
<li>System.Threading.ManualResetEventSlim是System.Threading.ManualResetEvent的轻量级版本。 </li>
<li>System.Threading.CountdownEvent表示一个事件，当其计数为零时，该事件将变为信号。 </li>
<li>System.Threading.Barrier允许多个线程彼此同步，而无需主线程控制。屏障阻止每个线程继续，直到所有线程都达到指定的点</li>
</ol>
<h5 id="SpinWait"><a href="#SpinWait" class="headerlink" title="SpinWait"></a>SpinWait</h5><p>从.NET Framework 4 开始，您可以在线程必须等待事件发送信号或满足条件时使用 System.Threading.SpinWait 结构，但实际等待时间应小于通过使用等待句柄或以其他方式阻止当前线程所需的等待时间。通过使用SpinWait，您可以在等待时指定短时间等待，然后仅当在指定时间内未满足条件时才产生（例如，通过等待或休眠）。</p>
<pre><code>using System;
using System.Threading;
using System.Threading.Tasks;

class SpinWaitDemo
{
    // Demonstrates:
    //      SpinWait construction
    //      SpinWait.SpinOnce()
    //      SpinWait.NextSpinWillYield
    //      SpinWait.Count
    static void Main()
    {
        bool someBoolean = false;
        int numYields = 0;

        // First task: SpinWait until someBoolean is set to true
        Task t1 = Task.Factory.StartNew(() =&gt;
        {
            SpinWait sw = new SpinWait();
            while (!someBoolean)
            {
                // The NextSpinWillYield property returns true if
                // calling sw.SpinOnce() will result in yielding the
                // processor instead of simply spinning.
                if (sw.NextSpinWillYield) numYields++;
                sw.SpinOnce();
            }

            // As of .NET Framework 4: After some initial spinning, SpinWait.SpinOnce() will yield every time.
            Console.WriteLine(&quot;SpinWait called {0} times, yielded {1} times&quot;, sw.Count, numYields);
        });

        // Second task: Wait 100ms, then set someBoolean to true
        Task t2 = Task.Factory.StartNew(() =&gt;
        {
            Thread.Sleep(100);
            someBoolean = true;
        });

        // Wait for tasks to complete
        Task.WaitAll(t1, t2);
    }
}
</code></pre><h5 id="Interlocked"><a href="#Interlocked" class="headerlink" title="Interlocked"></a>Interlocked</h5><p>Interlocked 操作是通过 Interlocked 类的静态方法在内存上执行的简单原子操作。这些原子操作包括增加，增加和减少，交换，取决于比较的条件交换，以及在32位平台上读取64位值的操作。 </p>
<p><code>注：原子性的保证仅限于个别操作;当多个操作必须作为一个单元执行时，必须使用更粗粒度的同步机制。</code> </p>
<p>虽然这些操作都不是锁或信号，但它们可以用来构造锁和信号。由于它们是Windows操作系统的本机特性，因此 Interlocked 操作非常快速。 </p>
<p>Interlocked 操作可以与 volatile memory 一起使用，以编写展现出强大的非阻塞，高并发的应用程序。但是，它们需要复杂的低级编程，所以对于大多数用途而言，简单的 lock 是更好的选择。</p>
<p>下面是使用 Interlocked 类的例子：</p>
<pre><code>using System;
using System.Threading;

namespace InterlockedExchange_Example
{
    class MyInterlockedExchangeExampleClass
    {
        //0 for false, 1 for true.
        private static int usingResource = 0;

        private const int numThreadIterations = 5;
        private const int numThreads = 10;

        static void Main()
        {
            Thread myThread;
            Random rnd = new Random();

            for(int i = 0; i &lt; numThreads; i++)
            {
                myThread = new Thread(new ThreadStart(MyThreadProc));
                myThread.Name = String.Format(&quot;Thread{0}&quot;, i + 1);

                //Wait a random amount of time before starting next thread.
                Thread.Sleep(rnd.Next(0, 1000));
                myThread.Start();
            }
        }

        private static void MyThreadProc()
        {
            for(int i = 0; i &lt; numThreadIterations; i++)
            {
                UseResource();

                //Wait 1 second before next attempt.
                Thread.Sleep(1000);
            }
        }

        //A simple method that denies reentrancy.
        static bool UseResource()
        {
            //0 indicates that the method is not in use.
            if(0 == Interlocked.Exchange(ref usingResource, 1))
            {
                Console.WriteLine(&quot;{0} acquired the lock&quot;, Thread.CurrentThread.Name);

                //Code to access a resource that is not thread safe would go here.

                //Simulate some work
                Thread.Sleep(500);

                Console.WriteLine(&quot;{0} exiting lock&quot;, Thread.CurrentThread.Name);

                //Release the lock
                Interlocked.Exchange(ref usingResource, 0);
                return true;
            }
            else
            {
                Console.WriteLine(&quot;   {0} was denied the lock&quot;, Thread.CurrentThread.Name);
                return false;
            }
        }

    }
}  
</code></pre><h4 id="Foreground-and-Background-Threads"><a href="#Foreground-and-Background-Threads" class="headerlink" title="Foreground and Background Threads"></a>Foreground and Background Threads</h4><p>托管线程可以是后台线程或前台线程。后台线程与前台线程相同，只有一个例外：后台线程不保持托管执行环境的运行。一旦所有前台线程在托管进程中停止，系统将停止所有后台线程并关闭。</p>
<p><strong>注意</strong>:当运行时由于进程关闭而停止后台线程时，线程中不会引发异常。但是，当线程由于AppDomain.Unload方法卸载应用程序域而停止时，ThreadAbortException会在前台和后台线程中抛出。</p>
<p>使用Thread.IsBackground属性来确定线程是后台线程还是前台线程，还是更改其状态。通过将其IsBackground属性设置为true，可以随时将线程更改为后台线程。</p>
<p>属于托管线程池的线程（即其IsThreadPoolThread属性为true的线程）是后台线程。所有从非托管代码进入托管执行环境的线程都被标记为后台线程。通过创建和启动一个新的Thread对象生成的所有线程默认情况下都是前台线程。</p>
<p>如果您使用线程来监视某个活动（如套接字连接），请将其IsBackground属性设置为true，以便该线程不会阻止您的进程终止。</p>
<h4 id="线程取消的机制"><a href="#线程取消的机制" class="headerlink" title="线程取消的机制"></a>线程取消的机制</h4><p>从.NET Framework 4开始，.NET Framework使用统一模型来协同取消异步或长时间运行的同步操作。该模型基于称为 cancellation token 的轻量级对象。调用一个或多个可取消操作的对象（例如通过创建新线程或任务）将 token 传递给每个操作。单独的操作可以将令牌的副本传递给其他操作。稍后，创建令牌的对象可以用它来请求操作停止他们正在做的事情。只有请求对象才能发出取消请求，并且每个监听者都有责任注意请求并以适当和及时的方式作出响应。</p>
<p>实施协作取消模式的一般模式是：</p>
<ol>
<li>实例化一个CancellationTokenSource对象，该对象管理并向各个取消令牌发送取消通知。</li>
<li>将CancellationTokenSource.Token属性返回的令牌传递给侦听取消的每个任务或线程。</li>
<li>为每个任务或线程提供响应取消的机制。</li>
<li>调用CancellationTokenSource.Cancel方法来提供取消通知。</li>
</ol>
<p><strong>需要注意</strong> CancellationTokenSource类实现了IDisposable接口。 当您完成使用取消标记来源以释放其拥有的任何非托管资源时，应确保调用CancellationTokenSource.Dispose方法。</p>
<p>示例代码：</p>
<pre><code>using System;
using System.Threading;

public class Example
{
public static void Main()
{
    // Create the token source.
    CancellationTokenSource cts = new CancellationTokenSource();

    // Pass the token to the cancelable operation.
    ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomeWork), cts.Token);
    Thread.Sleep(2500);

    // Request cancellation.
    cts.Cancel();
    Console.WriteLine(&quot;Cancellation set in token source...&quot;);
    Thread.Sleep(2500);
    // Cancellation should have happened, so call Dispose.
    cts.Dispose();
}

// Thread 2: The listener
static void DoSomeWork(object obj)
{
    CancellationToken token = (CancellationToken)obj;

    for (int i = 0; i &lt; 100000; i++) {
        if (token.IsCancellationRequested)
        {
            Console.WriteLine(&quot;In iteration {0}, cancellation has been requested...&quot;,
                            i + 1);
            // Perform cleanup if necessary.
            //...
            // Terminate the operation.
            break;
        }
        // Simulate some work.
        Thread.SpinWait(500000);
    }
}
}
// The example displays output like the following:
//       Cancellation set in token source...
//       In iteration 1430, cancellation has been requested...
</code></pre><h4 id="托管线程的最佳实践"><a href="#托管线程的最佳实践" class="headerlink" title="托管线程的最佳实践"></a>托管线程的最佳实践</h4><h5 id="死锁和竞态条件"><a href="#死锁和竞态条件" class="headerlink" title="死锁和竞态条件"></a>死锁和竞态条件</h5><p>死锁：当两个线程中的每一个尝试锁定另一个已锁定的资源时，都会发生死锁。 这两个线程都无法取得进一步的进展。</p>
<p>托管线程类的许多方法都会提供超时来帮助您检测死锁。 例如，以下代码尝试获取名为lockObject的对象的锁定。 如果锁定未在300毫秒内获得，则Monitor.TryEnter返回false。</p>
<pre><code>if (Monitor.TryEnter(lockObject, 300)) {  
    try {  
        // Place code protected by the Monitor here.  
    }  
    finally {  
        Monitor.Exit(lockObject);  
    }  
}  
else {  
    // Code to execute if the attempt times out.  
}  
</code></pre><h5 id="竟态条件"><a href="#竟态条件" class="headerlink" title="竟态条件"></a>竟态条件</h5><p>通过使用Interlocked类的方法（如Interlocked.Increment）可以轻松避免竞争条件。要了解用于在多个线程之间同步数据的其他技术，请参阅同步多线程数据。</p>
<p>当您同步多个线程的活动时，也可能发生竞争状况。每当你写一行代码时，你必须考虑如果一个线程在执行该行之前被抢先（或者在组成该行的任何单个机器指令之前）并且另一个线程超过它，可能发生的情况。</p>
<h4 id="进阶-Thread-中的异常处理"><a href="#进阶-Thread-中的异常处理" class="headerlink" title="进阶 - Thread 中的异常处理"></a>进阶 - Thread 中的异常处理</h4><p><a href="https://docs.microsoft.com/en-us/dotnet/standard/threading/exceptions-in-managed-threads" target="_blank" rel="noopener">Exceptions in Managed Threads</a></p>
<h4 id="进阶-Thread-Local-Storage"><a href="#进阶-Thread-Local-Storage" class="headerlink" title="进阶 - Thread Local Storage"></a>进阶 - Thread Local Storage</h4><p><a href="https://docs.microsoft.com/en-us/dotnet/standard/threading/thread-local-storage-thread-relative-static-fields-and-data-slots" target="_blank" rel="noopener">Thread Local Storage: Thread-Relative Static Fields and Data Slots</a></p>
<h3 id="2-使用线程"><a href="#2-使用线程" class="headerlink" title="2. 使用线程"></a>2. 使用线程</h3><h3 id="3-线程对象和特性"><a href="#3-线程对象和特性" class="headerlink" title="3. 线程对象和特性"></a>3. 线程对象和特性</h3>
          
        
      
    </div>

    

    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://sdfsd23few23.gitee.io/my_blog/my_blog/2018/06/24/DotNet-Guide-Parallel-Programming/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Raymond">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/my_blog/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Raymond's Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/my_blog/2018/06/24/DotNet-Guide-Parallel-Programming/" itemprop="url">
                  Parallel Processing, Concurrency, and Async Programming in .NET - 3
                </a>
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Veröffentlicht am</span>
              

              
                
              

              <time title="Post created: 2018-06-24 18:12:14" itemprop="dateCreated datePublished" datetime="2018-06-24T18:12:14+08:00">2018-06-24</time>
            

            
              

              
                
                <span class="post-meta-divider">|</span>
                

                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                
                  <span class="post-meta-item-text">Edited on</span>
                
                <time title="Updated at: 2018-06-27 21:42:47" itemprop="dateModified" datetime="2018-06-27T21:42:47+08:00">2018-06-27</time>
              
            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">in</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/my_blog/categories/Coding/" itemprop="url" rel="index"><span itemprop="name">Coding</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h1 id="NET-中的异步-多线程-并发处理-3-并行处理"><a href="#NET-中的异步-多线程-并发处理-3-并行处理" class="headerlink" title=".NET 中的异步/多线程/并发处理(3) - 并行处理"></a>.NET 中的异步/多线程/并发处理(3) - 并行处理</h1><p>本文核心：</p>
<ol>
<li>如何使用户交互界面具有良好的响应？</li>
<li>如何最大化利用CPU的性能？</li>
</ol>
<p>针对上面两个问题，根据 .NET Guide 中的 <a href="https://docs.microsoft.com/en-us/dotnet/standard/parallel-processing-and-concurrency" target="_blank" rel="noopener">Parallel Processing, Concurrency, and Async Programming in .NET</a> 的内容，进一步深入理解 .NET 的下面三种机制：</p>
<ol>
<li>异步编程</li>
<li>并行处理</li>
<li>多线程与线程同步机制。</li>
</ol>
<h2 id="并行处理"><a href="#并行处理" class="headerlink" title="并行处理"></a>并行处理</h2><p>许多个人电脑和工作站都有多个CPU内核，可以同时执行多个线程。预计不久的将来，计算机将拥有更多的内核。为了充分利用现在和未来的硬件，您可以并行化您的代码以跨多个处理器执行。在过去，并行化需要对线程和锁进行低级处理。.NET Framework 4 通过提供新的运行时，新的类库类型和新的诊断工具来增强对并行编程的支持。这些特性简化了并行开发，因此您可以编写高效，细化和可扩展的并行代码，而无需直接与线程或线程池一起工作。以下插图提供了.NET Framework 4 中并行编程体系结构的高级概述。</p>
<p><img src="/my_blog/2018/06/24/DotNet-Guide-Parallel-Programming/tpl-architecture.png" alt="TPL-Architecture"></p>
<h3 id="Task-Parallel-Library-TPL"><a href="#Task-Parallel-Library-TPL" class="headerlink" title="Task Parallel Library (TPL)"></a>Task Parallel Library (TPL)</h3><p>TPL 是 System.Threading 和 System.Threading.Tasks 命名空间中的一组公共类型和 API。TPL动态调整并发度以最有效地使用所有可用的处理器。另外，TPL替你完成一般的并行处理任务中的：任务分派，ThreadPool上线程的调度，取消功能的支持，状态管理以及其他低级细节。通过使用TPL，您可以最大限度地提高代码的性能，同时专注于您的程序旨在完成的工作。</p>
<p>但是，并非所有代码都适合并行化;例如，如果循环在每次迭代中仅执行少量工作，或者它不会运行多次迭代，那么并行化的开销会导致代码运行速度更慢。此外，像任何多线程代码一样的并行化会增加程序执行的复杂性。尽管TPL简化了多线程场景，但起码要对线程概念（例如线程锁，死锁和竞态条件）有基本的了解，以便您可以有效地使用TPL。</p>
<p><code>竟态条件（race condition）：当两个或更多线程可以访问共享数据并尝试同时更改共享数据时，会发生争用情况。</code></p>
<h3 id="Data-Parallelism"><a href="#Data-Parallelism" class="headerlink" title="Data Parallelism"></a>Data Parallelism</h3><pre><code>// Sequential version            
foreach (var item in sourceCollection)
{
    Process(item);
}

// Parallel equivalent
Parallel.ForEach(sourceCollection, item =&gt; Process(item));
</code></pre><p><em>Data Parallelism</em> 是指对源集合或数组中的元素并行（即并行）执行相同操作的场景。在数据并行操作中，对源集合进行分区，以便多个线程可以同时在不同的分段上运行。</p>
<p>任务并行库（TPL）通过System.Threading.Tasks.Parallel类支持数据并行性。此类提供for和foreach循环的基于方法的并行实现（For和For Each在Visual Basic中）。您可以编写Parallel.For或Parallel.ForEach循环的循环逻辑，就像编写循环循环一样。您不必创建线程或排队工作项目。在基本循环中，您不必锁定。 TPL为您处理所有低级别工作。有关使用Parallel.For和Parallel.ForEach的详细信息，请下载用于并行编程的模式：使用.NET Framework 4了解和应用并行模式。以下代码示例显示了一个简单的foreach循环及其并行等效。</p>
<p>当并行循环运行时，TPL分割数据源，以便循环可以同时在多个部分上运行。在幕后，Task Scheduler 根据系统资源和工作负载对任务进行分区。如果可能，如果工作负载变得不平衡，则调度程序会在多个线程和处理器之间重新分配工作。</p>
<p>Parallel.For和Parallel.ForEach方法都有几个重载，这些重载允许您停止或中断循环执行，监视其他线程上的循环状态，维护线程本地状态，定位线程本地对象，控制并发程度，等等。启用此功能的助手类型包括ParallelLoopState，ParallelOptions，ParallelLoopResult，CancellationToken和CancellationTokenSource。</p>
<p>具有声明式或查询式语法的数据并行性由PLINQ支持。有关更多信息，请参阅并行LINQ（PLINQ）。</p>
<h4 id="当循环体的计算比较简单时的处理办法"><a href="#当循环体的计算比较简单时的处理办法" class="headerlink" title="当循环体的计算比较简单时的处理办法"></a>当循环体的计算比较简单时的处理办法</h4><p>当 Parallel.Foreach 循环体的计算比较简单时，它可能比等效的顺序循环执行得更慢。 性能降低是由于分区数据所涉及的开销以及在每次循环迭代中调用委托的成本造成的。 为了解决这种情况，Partitioner类提供了Partitioner.Create方法，该方法使您可以为委托体提供一个顺序循环，以便每个分区仅调用一次委托，而不是每次迭代一次调用一次。</p>
<pre><code>using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {

        // Source must be array or IList.
        var source = Enumerable.Range(0, 100000).ToArray();

        // Partition the entire source array.
        var rangePartitioner = Partitioner.Create(0, source.Length);

        double[] results = new double[source.Length];

        // Loop over the partitions in parallel.
        Parallel.ForEach(rangePartitioner, (range, loopState) =&gt;
        {
            // Loop over each range element without a delegate invocation.
            for (int i = range.Item1; i &lt; range.Item2; i++)
            {
                results[i] = source[i] * Math.PI;
            }
        });

        Console.WriteLine(&quot;Operation complete. Print results? y/n&quot;);
        char input = Console.ReadKey().KeyChar;
        if (input == &apos;y&apos; || input == &apos;Y&apos;)
        {
            foreach(double d in results)
            {
                Console.Write(&quot;{0} &quot;, d);
            }           
        }
    }
}
</code></pre><h3 id="TPL-中的-Dataflow"><a href="#TPL-中的-Dataflow" class="headerlink" title="TPL 中的 Dataflow"></a>TPL 中的 Dataflow</h3><p>任务并行库（TPL）提供 Dataflow 来帮助提高启用并发功能的应用程序的健壮性。这些 Dataflow 统称为TPL数据流库。此数据流模型通过为粗粒度数据流和流水线任务提供进程内消息传递来促进基于角色的编程。数据流组件基于TPL的类型和调度基础结构，并与异步编程的C＃，Visual Basic和F＃语言支持集成。当您有多个必须异步通信或需要处理数据的数据流时，这些数据流组件非常有用。例如，考虑处理来自 webCam 的图像数据的应用程序。通过使用数据流模型，应用程序可以在图像帧可用时对其进行处理。如果应用程序增强了图像帧，例如通过执行光校正或减轻红眼，则可以创建数据流组件的管道。流水线的每个阶段都可能使用更多粗粒度并行功能，例如TPL提供的功能来转换图像。</p>
<p><a href="https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/dataflow-task-parallel-library" target="_blank" rel="noopener">Dataflow</a></p>

          
        
      
    </div>

    

    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://sdfsd23few23.gitee.io/my_blog/my_blog/2018/06/20/DotNet-Guide-Async-Programming/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Raymond">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/my_blog/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Raymond's Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/my_blog/2018/06/20/DotNet-Guide-Async-Programming/" itemprop="url">
                  Parallel Processing, Concurrency, and Async Programming in .NET - 2
                </a>
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Veröffentlicht am</span>
              

              
                
              

              <time title="Post created: 2018-06-20 18:56:58" itemprop="dateCreated datePublished" datetime="2018-06-20T18:56:58+08:00">2018-06-20</time>
            

            
              

              
                
                <span class="post-meta-divider">|</span>
                

                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                
                  <span class="post-meta-item-text">Edited on</span>
                
                <time title="Updated at: 2018-06-24 19:19:59" itemprop="dateModified" datetime="2018-06-24T19:19:59+08:00">2018-06-24</time>
              
            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">in</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/my_blog/categories/Coding/" itemprop="url" rel="index"><span itemprop="name">Coding</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h1 id="NET-中的异步-多线程-并发处理-2-异步"><a href="#NET-中的异步-多线程-并发处理-2-异步" class="headerlink" title=".NET 中的异步/多线程/并发处理(2) - 异步"></a>.NET 中的异步/多线程/并发处理(2) - 异步</h1><p>本文核心：</p>
<ol>
<li>如何使用户交互界面具有良好的响应？</li>
<li>如何最大化利用CPU的性能？</li>
</ol>
<p>针对上面两个问题，根据 .NET Guide 中的 <a href="https://docs.microsoft.com/en-us/dotnet/standard/parallel-processing-and-concurrency" target="_blank" rel="noopener">Parallel Processing, Concurrency, and Async Programming in .NET</a> 的内容，进一步深入理解 .NET 的下面三种机制：</p>
<ol>
<li>异步编程</li>
<li>并行处理</li>
<li>多线程与线程同步机制。</li>
</ol>
<h2 id="异步编程"><a href="#异步编程" class="headerlink" title="异步编程"></a>异步编程</h2><p>异步编程的核心在于将所有阻塞主线程（UI线程）的操作，放到后台线程中执行，使程序具有良好的响应。</p>
<p>.NET 中的异步编程具有三种模式（pattern）：</p>
<ol>
<li>Asynchronous Programming Model (APM) pattern (also called the IAsyncResult pattern)</li>
<li>Event-based Asynchronous Pattern (EAP)</li>
<li>Task-based Asynchronous Pattern (TAP)</li>
</ol>
<h3 id="1-APM模式"><a href="#1-APM模式" class="headerlink" title="1. APM模式"></a>1. APM模式</h3><p>此模式使用 <a href="https://docs.microsoft.com/en-us/dotnet/api/system.iasyncresult" target="_blank" rel="noopener">IAsyncResult</a> 接口，并通过成对的 Begin<em>OperationName</em> 和 End<em>OperationName</em> 方法开始/终止异步操作。</p>
<pre><code>例：FileStream 提供 BeginRead 和 EndRead 方法来读取文件内容
</code></pre><p>在调用 Begin<em>OperationName</em> 方法后，调用线程会继续向下执行，而异步操作会在另一个线程中执行。每次调用 Begin<em>OperationName</em> 后，都应调用 End<em>OperationName</em> 来获取异步操作的结果。</p>
<p>需要注意：当调用 End<em>OperationName</em> 时异步操作还没有完成，那么调用线程会等待，直到异步操作完成。异步操作中出现的异常会从 End<em>OperationName</em> 方法中抛出。</p>
<p>采用阻塞的方式的两个例子：</p>
<ol>
<li><a href="https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/blocking-application-execution-by-ending-an-async-operation" target="_blank" rel="noopener">Blocking Application Execution by Ending an Async Operation</a></li>
<li><a href="https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/blocking-application-execution-using-an-asyncwaithandle" target="_blank" rel="noopener">Blocking Application Execution Using an AsyncWaitHandle</a></li>
</ol>
<p>采用非阻塞有两种方式，一种是监控IsComplete属性，一种是使用使用AysncCallback函数的方式：</p>
<ol>
<li><a href="https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/polling-for-the-status-of-an-asynchronous-operation" target="_blank" rel="noopener">Polling for the Status of an Asynchronous Operation</a></li>
<li><a href="https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/using-an-asynccallback-delegate-to-end-an-asynchronous-operation" target="_blank" rel="noopener">Using an AsyncCallback Delegate to End an Asynchronous Operation</a></li>
</ol>
<h3 id="2-基于事件的异步"><a href="#2-基于事件的异步" class="headerlink" title="2. 基于事件的异步"></a>2. 基于事件的异步</h3><p>如果你的程序需要同时处理多个任务，那么System.Threading命名空间中能满足大部分复杂的需求,但是如果你的需求比较简单，那么可以考虑使用基于事件的异步，一个直接的例子是 <a href="https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker" target="_blank" rel="noopener">BackgroundWorker</a>，它能完成一个后台任务，但是如果你需要同时运行多个独立的任务，那么可以考虑实现一个符合基于事件异步设计模式。</p>
<p>基于事件的异步操作设计模式，一般包含一对或多对 <em>MethodName</em>Async 方法和 <em>MethodName</em>Completed 事件。如果想要支持取消异步操作的功能，则可以拥有一个或多个<em>MethodName</em>CancelAsync 方法。而具体实现方式通过多线程，这部分在后边会提到。</p>
<h3 id="3-基于任务的异步"><a href="#3-基于任务的异步" class="headerlink" title="3. 基于任务的异步"></a>3. 基于任务的异步</h3><p>基于任务的异步的核心是 System.Threading.Tasks.Task 和 System.Threading.Tasks.Task<tresult> 类型。</tresult></p>
<ul>
<li><strong>Task</strong> 表示一个没有返回类型的任务</li>
<li><strong>Task\<t></t></strong> 表示一个具有返回类型为T的任务</li>
</ul>
<p>基于任务的异步是微软推荐的异步编程模型。</p>
<p>微软还提供了 async 和 await 关键字（C# 5.0 &amp; .NET Framework 4.5) 来大幅度简化异步编程，它们与 Task 一起使用，就能编写出简单、直观、易读、稳定性高的异步操作代码。</p>
<h4 id="async-await-关键字"><a href="#async-await-关键字" class="headerlink" title="async/await 关键字"></a>async/await 关键字</h4><p>使用 async 和 await 关键字可以让你用编写同步操作一样的方式，编写异步操作。一个异步方法使用 async 关键字修饰，方法体内部会有使用 await 关键字修饰的方法，下面我们通过一个例子说明如何使用这两个关键字以及使用这种编程方式的优势。</p>
<pre><code>// Mark the event handler with async so you can use await in it.
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    // Call and await separately.
    //Task&lt;int&gt; getLengthTask = AccessTheWebAsync();
    //// You can do independent work here.
    //int contentLength = await getLengthTask;

    int contentLength = await AccessTheWebAsync();

    resultsTextBox.Text +=
        String.Format(&quot;\r\nLength of the downloaded string: {0}.\r\n&quot;, contentLength);

}

// Three things to note in the signature:
//  - The method has an async modifier. 
//  - The return type is Task or Task&lt;T&gt;. (See &quot;Return Types&quot; section.)
//    Here, it is Task&lt;int&gt; because the return statement returns an integer.
//  - The method name ends in &quot;Async.&quot;
async Task&lt;int&gt; AccessTheWebAsync()
{ 
    // You need to add a reference to System.Net.Http to declare client.
    HttpClient client = new HttpClient();

    // GetStringAsync returns a Task&lt;string&gt;. That means that when you await the
    // task you&apos;ll get a string (urlContents).
    Task&lt;string&gt; getStringTask = client.GetStringAsync(&quot;http://msdn.microsoft.com&quot;);

    // You can do work here that doesn&apos;t rely on the string from GetStringAsync.
    DoIndependentWork();

    // The await operator suspends AccessTheWebAsync.
    //  - AccessTheWebAsync can&apos;t continue until getStringTask is complete.
    //  - Meanwhile, control returns to the caller of AccessTheWebAsync.
    //  - Control resumes here when getStringTask is complete. 
    //  - The await operator then retrieves the string result from getStringTask.
    string urlContents = await getStringTask;

    // The return statement specifies an integer result.
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value.
    return urlContents.Length;
}

void DoIndependentWork()
{
    resultsTextBox.Text += &quot;Working . . . . . . .\r\n&quot;;
}
</code></pre><p>上面的例子主要完成：点击按钮后，加载一个网页，在网页加载完成后将网页的字节数显示在 TextBox 中。</p>
<p>可以看到如果把 async 和 await 关键字去掉，这就是一个同步处理的过程，调用一个方法加载网页，加载后获取字节数，然后显示。</p>
<p>根据上面的代码我们总结一下一个 async 方法的特点：</p>
<ol>
<li>使用 async 关键字修饰方法声明</li>
<li>方法名称使用“Async”后缀</li>
<li>返回类型为：Task/Task\<t></t></li>
<li>方法体内至少有一个 await 语句，说明本方法应等待哪个异步方法完成。</li>
</ol>
<h4 id="async-方法的执行时序"><a href="#async-方法的执行时序" class="headerlink" title="async 方法的执行时序"></a>async 方法的执行时序</h4><p><img src="/my_blog/2018/06/20/DotNet-Guide-Async-Programming/async_workflow.png" alt="执行时序" title="async 方法的执行时序"></p>
<h4 id="async-方法中的线程"><a href="#async-方法中的线程" class="headerlink" title="async 方法中的线程"></a>async 方法中的线程</h4><p>当程序执行到 async 方法中的 await 语句修饰的方法时，而调用线程会立刻返回，而剩余的语句会在被 await 修饰的方法完成后，再执行。需要注意的是，上述过程并不会创建额外的线程（在我阅读到这部分时十分困惑），异步方法不要求多线程，因为异步方法不会创建一个线程并运行在创建的线程上，而是运行在UI线程（SynchronizationContext）。对于CPU-bound（后续详解）任务来说，建立一个其他线程不会对减少运行时间有所帮助。</p>
<p>但是对于ConsoleApp来说情况有点不一样，因为只有窗体程序（Winform,WPF..)才会有 SynchronizationContext 这个对象，对于ConsoleApp来说异步方法是会运行在一个其他线程上的。在我刚阅读到这段的时候，我还专门做了个实验，发现居然和文章中说的不一样，我还以为我理解错了，后来在 Stackoverflow 的找到有个人和我有一样的问题 <a href="https://stackoverflow.com/questions/40249169/async-await-and-threading" target="_blank" rel="noopener">async await and threading</a>。这里我贴一下正确的回答:</p>
<p><code>the way that async/await works in a console application is very different than how it works in a UI application due to the lack of a synchronization context in a console application.</code><br><a href="https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/" target="_blank" rel="noopener">This article</a><br><code>describes details and gives a code sample of how add one so that async/await behaves in a more predictable way.</code></p>
<h4 id="I-O-bound-和-CPU-bound"><a href="#I-O-bound-和-CPU-bound" class="headerlink" title="I/O-bound 和 CPU-bound"></a>I/O-bound 和 CPU-bound</h4><p>计算机的两种典型操作：I/O-bound 和 CPU-bound。</p>
<p>I/O-bound (I/O密集型)：涉及到网络、磁盘IO的任务都是IO密集型任务，这类任务的特点是CPU消耗很少，任务的大部分时间都在等待IO操作完成（因为IO的速度远远低于CPU和内存的速度）。对于IO密集型任务，任务越多，CPU效率越高，但也有一个限度。</p>
<p>CPU-bound (计算密集型)操作：计算密集型任务的特点是要进行大量的计算，消耗CPU资源，比如计算圆周率、对视频进行高清解码等等，全靠CPU的运算能力。这种计算密集型任务虽然也可以用多任务完成，但是任务越多，花在任务切换的时间就越多，CPU执行任务的效率就越低，所以，要最高效地利用CPU，计算密集型任务同时进行的数量应当等于CPU的核心数。</p>
<p>下面的代码是 I/O-bound 和 CPU-bound 两种情况下的基于任务的异步模式的例子，通过代码说明基于任务的异步模式的优点和特性。</p>
<h4 id="I-O-bound-Task"><a href="#I-O-bound-Task" class="headerlink" title="I/O-bound Task"></a>I/O-bound Task</h4><p>下面的代码是一个典型的异步I/O操作，GetHtmlAsync 方法返回一个可以运行的Task。</p>
<pre><code>public Task&lt;string&gt; GetHtmlAsync()
{
    // Execution is synchronous here
    var client = new HttpClient();

    return client.GetStringAsync(&quot;http://www.dotnetfoundation.org&quot;);
}
</code></pre><p>第二个例子使用 async 和 await 操作Task。</p>
<pre><code>public async Task&lt;string&gt; GetFirstCharactersCountAsync(string url, int count)
{
    // Execution is synchronous here
    var client = new HttpClient();

    // Execution of GetFirstCharactersCountAsync() is yielded to the caller here
    // GetStringAsync returns a Task&lt;string&gt;, which is *awaited*
    var page = await client.GetStringAsync(&quot;http://www.dotnetfoundation.org&quot;);

    // Execution resumes when the client.GetStringAsync task completes,
    // becoming synchronous again.

    if (count &gt; page.Length)
    {
        return page;
    }
    else
    {
        return page.Substring(0, count);
    }
}
</code></pre><p>通过上面这两种基于任务（Task）的异步模式处理I/O操作，操作系统就能利用 Interrupt Request Packet (IRP)的方式，使宝贵的CPU时间，不浪费在等待缓慢的I/O操作完成上。</p>
<p>虽然看上去有另一个线程在处理I/O操作，但其实没有任何线程专门运行这个Task，也就是说没有一个线程是在等待I/O操作完成。一个I/O操作的Task的执行过程是：某个线程调用操作系统底层I/O驱动，向驱动发送操作指令，然后直接返回（此时本线程退出），驱动最终完成I/O操作完成后通过 IRP 通知操作系统，然后操作系统在把数据交给一个可用线程进行处理，最终把结果返回给调用者。</p>
<p>上述的过程，最终效果就是CPU能处理大量的I/O操作，而不是把时间浪费在等待I/O操作结束上。</p>
<p>对服务端来说，比如一个网页服务器，它的意义在于能更高效的利用CPU，处理更多的客户端请求。</p>
<p>对于客户端来说，虽然你也可以通过手动增加后台线程的方式实现异步操作，使你的程序具有良好的界面响应，但是相比于使用 await 和 async 的 Task 方式的异步模式，效率更低也更加复杂。还有一方面是因为I/O操作基本不占用CPU，如果你把一个线程专门用来等待I/O操作是一种资源的浪费。</p>
<h4 id="CPU-bound-Task"><a href="#CPU-bound-Task" class="headerlink" title="CPU-bound Task"></a>CPU-bound Task</h4><p>CPU-bound的异步代码和I/O-bound的代码不同，因为任务需要CPU来执行，所以必定需要指定一个线程来进行计算，所以对于此类任务，async 和 await 的作用只是给你提供一个简单清晰的方法去操作后台线程。</p>
<p>下面是一个CPU-bound 异步调用：</p>
<pre><code>public async Task&lt;int&gt; CalculateResult(InputData data)
{
    // This queues up the work on the threadpool.
    var expensiveResultTask = Task.Run(() =&gt; DoExpensiveCalculation(data));

    // Note that at this point, you can do some other work concurrently,
    // as CalculateResult() is still executing!

    // Execution of CalculateResult is yielded here!
    var result = await expensiveResultTask;

    return Math.Abs(result);
}
</code></pre><p>可以看到，代码使用了 Task.Run 将 DoExpensiveCalculation 的计算加入 ThreadPool 中去运行（可能会是在另一个CPU核心上）。</p>
<h4 id="监控进度"><a href="#监控进度" class="headerlink" title="监控进度"></a>监控进度</h4><p>监控进度的例子：</p>
<pre><code>private async void btnDownload_Click(object sender, RoutedEventArgs e)    
{  
    btnDownload.IsEnabled = false;  
    try  
    {  
        txtResult.Text = await DownloadStringAsync(txtUrl.Text,   
            new Progress&lt;int&gt;(p =&gt; pbDownloadProgress.Value = p));  
    }  
    finally { btnDownload.IsEnabled = true; }  
}
</code></pre><h4 id="进阶-async-方法中的并行处理"><a href="#进阶-async-方法中的并行处理" class="headerlink" title="进阶 - async 方法中的并行处理"></a>进阶 - async 方法中的并行处理</h4><p><a href="https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern#taskfromresult" target="_blank" rel="noopener">Task.FromResult</a></p>
<p><a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/how-to-extend-the-async-walkthrough-by-using-task-whenall" target="_blank" rel="noopener">Task.WhenAll</a></p>
<p><a href="https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern#redundancy" target="_blank" rel="noopener">Redundancy</a></p>
<p><a href="https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern#taskwhenany" target="_blank" rel="noopener">Task.WhenAny</a></p>
<p><a href="https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern#interleaving" target="_blank" rel="noopener">Interleaving</a></p>
<p><a href="https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern#throttling" target="_blank" rel="noopener">Throttling</a></p>
<p><a href="https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern#taskdelay" target="_blank" rel="noopener">Task.Delay</a></p>
<p><a href="https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern#early-bailout" target="_blank" rel="noopener">Early Bailout</a></p>
<p><a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/how-to-make-multiple-web-requests-in-parallel-by-using-async-and-await" target="_blank" rel="noopener">Parallel by Using async and await</a></p>

          
        
      
    </div>

    

    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://sdfsd23few23.gitee.io/my_blog/my_blog/2018/04/09/CSharp-to-Java-2/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Raymond">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/my_blog/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Raymond's Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/my_blog/2018/04/09/CSharp-to-Java-2/" itemprop="url">
                  CSharp to Java #2
                </a>
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Veröffentlicht am</span>
              

              
                
              

              <time title="Post created: 2018-04-09 23:58:18" itemprop="dateCreated datePublished" datetime="2018-04-09T23:58:18+08:00">2018-04-09</time>
            

            
              

              
                
                <span class="post-meta-divider">|</span>
                

                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                
                  <span class="post-meta-item-text">Edited on</span>
                
                <time title="Updated at: 2018-04-12 23:42:43" itemprop="dateModified" datetime="2018-04-12T23:42:43+08:00">2018-04-12</time>
              
            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">in</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/my_blog/categories/Coding/" itemprop="url" rel="index"><span itemprop="name">Coding</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h1 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h1><p><a href="https://www.cnblogs.com/lchzls/p/6714335.html" target="_blank" rel="noopener">HashMap和Hashtable区别</a> </p>

          
        
      
    </div>

    

    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://sdfsd23few23.gitee.io/my_blog/my_blog/2018/03/14/Java小技巧备忘录/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Raymond">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/my_blog/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Raymond's Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/my_blog/2018/03/14/Java小技巧备忘录/" itemprop="url">
                  Java小技巧备忘录
                </a>
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Veröffentlicht am</span>
              

              
                
              

              <time title="Post created: 2018-03-14 21:35:03" itemprop="dateCreated datePublished" datetime="2018-03-14T21:35:03+08:00">2018-03-14</time>
            

            
              

              
                
                <span class="post-meta-divider">|</span>
                

                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                
                  <span class="post-meta-item-text">Edited on</span>
                
                <time title="Updated at: 2018-04-10 21:33:24" itemprop="dateModified" datetime="2018-04-10T21:33:24+08:00">2018-04-10</time>
              
            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">in</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/my_blog/categories/Coding/" itemprop="url" rel="index"><span itemprop="name">Coding</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p>总结一些平常遇见的小技巧和小知识点</p>
<h1 id="List"><a href="#List" class="headerlink" title="List"></a>List</h1><p>初始化List的方法</p>
<pre><code>List&lt;Integer&gt; intList = Arrays.asList(1,2,3);
</code></pre><h1 id="Arrays"><a href="#Arrays" class="headerlink" title="Arrays"></a>Arrays</h1><p>Arrays.sort() 采用的是Dual-Pivot Quicksort，有时间可以分析一下源码</p>
<p>Arrays.binarySort() 自带的二分查找</p>

          
        
      
    </div>

    

    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://sdfsd23few23.gitee.io/my_blog/my_blog/2018/03/12/CSharp-to-Java-1/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Raymond">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/my_blog/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Raymond's Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/my_blog/2018/03/12/CSharp-to-Java-1/" itemprop="url">
                  CSharp to Java #1
                </a>
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Veröffentlicht am</span>
              

              
                
              

              <time title="Post created: 2018-03-12 21:56:45" itemprop="dateCreated datePublished" datetime="2018-03-12T21:56:45+08:00">2018-03-12</time>
            

            
              

              
                
                <span class="post-meta-divider">|</span>
                

                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                
                  <span class="post-meta-item-text">Edited on</span>
                
                <time title="Updated at: 2018-03-20 23:39:50" itemprop="dateModified" datetime="2018-03-20T23:39:50+08:00">2018-03-20</time>
              
            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">in</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/my_blog/categories/Coding/" itemprop="url" rel="index"><span itemprop="name">Coding</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>从在学校起，一直接触的就是CSharp，算来也有5年的时间了，渐渐觉得自己进入了一个瓶颈。</p>
<p>平时虽然也一直在关注技术，但是总觉得那些新的，很有趣的技术真的很少于CSharp相关，加上工作也是比较封闭的环境，所以想开始学习一门新的语言。</p>
<p>说是学习新的语言，其实“编程语言”只是一个通向广阔技术世界的“桥梁”,我真的想要的是向真正的技术前沿靠近，趁着自己的热血没凉，继续前进，继续开阔眼界。</p>
<p>而选择Java也是因为Java与CSharp的十分相似，无论是语言本身还是这门语言的定位。</p>
<p>另外毕业这么久，技术博客也没坚持更新，借这个机会把自己CSharp转Java的过程记录一下。</p>
<h1 id="基础语法"><a href="#基础语法" class="headerlink" title="基础语法"></a>基础语法</h1><p>首先学习一下两个语言的基本语法差别。</p>
<h2 id="方法名称命名规则"><a href="#方法名称命名规则" class="headerlink" title="方法名称命名规则"></a>方法名称命名规则</h2><p>CS:首字母大写。</p>
<p>Java:首字母小写。</p>
<h2 id="源文件名"><a href="#源文件名" class="headerlink" title="源文件名"></a>源文件名</h2><p>Java:源文件名必须和类名相同。当保存文件的时候，你应该使用类名作为文件名保存（切记Java是大小写敏感的），文件名的后缀为.java。（如果文件名和类名不相同则会导致编译错误）。一个源文件中只能有一个public类，可以有多个非public类</p>
<p>CS:没有强制规定，建议一致。</p>
<h2 id="包与命名空间"><a href="#包与命名空间" class="headerlink" title="包与命名空间"></a>包与命名空间</h2><p>Java中包(Package)与CS中的命名空间类似，Java中的default对应CS中的internal。</p>
<h2 id="继承与实现接口的关键字"><a href="#继承与实现接口的关键字" class="headerlink" title="继承与实现接口的关键字"></a>继承与实现接口的关键字</h2><p>java的类继承关键字extends，接口为implements，CS都为’:’</p>
<h2 id="一些其他关键字"><a href="#一些其他关键字" class="headerlink" title="一些其他关键字"></a>一些其他关键字</h2><p>java中的final修饰类表示为封闭类，修饰方法时表示方法不能被重写，修饰变量的时候表示为常量</p>
<p>CS的seal表示封闭类，readonly和const表示常量</p>
<p>在java子类中用supper表示父类，C#用base</p>
<h2 id="String-amp-string"><a href="#String-amp-string" class="headerlink" title="String &amp; string"></a>String &amp; string</h2><h2 id="protected"><a href="#protected" class="headerlink" title="protected"></a>protected</h2><p>Java:同一包内的类和所有子类可见（不能修饰类和接口）<br>CS:包含类或者从包含类派生的类型（不能通过基类访问）</p>

          
        
      
    </div>

    

    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://sdfsd23few23.gitee.io/my_blog/my_blog/2018/03/12/hello-world/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Raymond">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/my_blog/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Raymond's Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/my_blog/2018/03/12/hello-world/" itemprop="url">
                  Hello World
                </a>
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Veröffentlicht am</span>
              

              
                
              

              <time title="Post created: 2018-03-12 21:07:21" itemprop="dateCreated datePublished" datetime="2018-03-12T21:07:21+08:00">2018-03-12</time>
            

            
              

              
                
                <span class="post-meta-divider">|</span>
                

                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                
                  <span class="post-meta-item-text">Edited on</span>
                
                <time title="Updated at: 2016-06-24 00:05:23" itemprop="dateModified" datetime="2016-06-24T00:05:23+08:00">2016-06-24</time>
              
            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">in</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/my_blog/categories/Testing/" itemprop="url" rel="index"><span itemprop="name">Testing</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p>Welcome to <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/" target="_blank" rel="noopener">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="noopener">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues" target="_blank" rel="noopener">GitHub</a>.</p>
<h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure>
<p>More info: <a href="https://hexo.io/docs/writing.html" target="_blank" rel="noopener">Writing</a></p>
<h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure>
<p>More info: <a href="https://hexo.io/docs/server.html" target="_blank" rel="noopener">Server</a></p>
<h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure>
<p>More info: <a href="https://hexo.io/docs/generating.html" target="_blank" rel="noopener">Generating</a></p>
<h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure>
<p>More info: <a href="https://hexo.io/docs/deployment.html" target="_blank" rel="noopener">Deployment</a></p>

          
        
      
    </div>

    

    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://sdfsd23few23.gitee.io/my_blog/my_blog/2016/06/25/搭建Windows环境下的HEXO-GithubPages个人博客/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Raymond">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/my_blog/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Raymond's Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/my_blog/2016/06/25/搭建Windows环境下的HEXO-GithubPages个人博客/" itemprop="url">
                  搭建Windows环境下的HEXO+GithubPages个人博客
                </a>
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Veröffentlicht am</span>
              

              
                
              

              <time title="Post created: 2016-06-25 16:31:07 / Updated at: 16:52:54" itemprop="dateCreated datePublished" datetime="2016-06-25T16:31:07+08:00">2016-06-25</time>
            

            
              

              
            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">in</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/my_blog/categories/网站建设/" itemprop="url" rel="index"><span itemprop="name">网站建设</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <p>花了几天，断断续续把博客建好了，顺便就总结下这几天折腾的过程。</p>
<p>环境：win10，Chrome浏览器。</p>
<h2 id="安装配置"><a href="#安装配置" class="headerlink" title="安装配置"></a>安装配置</h2><p>hexo需要Node.js和Git的支持，所以先去下载两者的Windows版本。正常安装即可。</p>
<p>使用cnpm代替npm。</p>
<p>使用Git建立自己的RSA秘钥。方法：</p>
<p>是否建立好Github Pages，官方文档（链接）</p>
<ol>
<li>建立名为“<em>username</em>.github.io”的repo，username为你github的用户名。注意，<em>username</em>必须完全和用户名一致，否则没有作用。</li>
<li>添加公钥。</li>
</ol>
<p>安装hexo</p>
<p>hexo基本命令行</p>
<p>建立新Post</p>
<p>安装插件</p>
<p>安装主题</p>
<h2 id="优化"><a href="#优化" class="headerlink" title="优化"></a>优化</h2><p>gulp安装</p>
<p>gulp模块安装</p>
<p>gulpfile.js编写</p>
<p>图片优化</p>
<p>Google insight</p>
<p>dns优化百度spider</p>
<p>百度统计</p>

          
        
      
    </div>

    

    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
  </section>

  
  <nav class="pagination">
    <span class="page-number current">1</span><a class="page-number" href="/my_blog/page/2/">2</a><a class="extend next" rel="next" href="/my_blog/page/2/"><i class="fa fa-angle-right" aria-label="Next page"></i></a>
  </nav>



          </div>
          

        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      

      <section class="site-overview-wrap sidebar-panel sidebar-panel-active">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name">Raymond</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          
            <nav class="site-state motion-element">
              
                <div class="site-state-item site-state-posts">
                
                  <a href="/my_blog/archives/">
                
                    <span class="site-state-item-count">12</span>
                    <span class="site-state-item-name">Artikel</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-categories">
                  <a href="/my_blog/categories/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">5</span>
                    <span class="site-state-item-name">Kategorien</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-tags">
                  <a href="/my_blog/Tags/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">11</span>
                    <span class="site-state-item-name">Tags</span>
                  </a>
                </div>
              
            </nav>
          

          

          

          
          

          
          

          
            
          
          

        </div>
      </section>

      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2018</span>
  <span class="with-love" id="animate">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Raymond</span>

  

  
</div>




  <div class="powered-by">Erstellt mit  <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> v3.7.1</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">Theme &mdash; <a class="theme-link" target="_blank" href="https://theme-next.org">NexT.Pisces</a> v6.3.0</div>




        








        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>


























  
  
    <script type="text/javascript" src="/my_blog/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/my_blog/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/my_blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  


  


  <script type="text/javascript" src="/my_blog/js/src/utils.js?v=6.3.0"></script>

  <script type="text/javascript" src="/my_blog/js/src/motion.js?v=6.3.0"></script>



  
  


  <script type="text/javascript" src="/my_blog/js/src/affix.js?v=6.3.0"></script>

  <script type="text/javascript" src="/my_blog/js/src/schemes/pisces.js?v=6.3.0"></script>



  

  


  <script type="text/javascript" src="/my_blog/js/src/bootstrap.js?v=6.3.0"></script>



  



	





  





  










  





  

  

  

  

  
  

  

  

  

  

  

</body>
</html>
